Skip to content

fix(filesystem): add optional depth parameter to list_directory#3882

Open
lui62233 wants to merge 1 commit intomodelcontextprotocol:mainfrom
lui62233:fix-list-directory-depth
Open

fix(filesystem): add optional depth parameter to list_directory#3882
lui62233 wants to merge 1 commit intomodelcontextprotocol:mainfrom
lui62233:fix-list-directory-depth

Conversation

@lui62233
Copy link
Copy Markdown

@lui62233 lui62233 commented Apr 9, 2026

Summary

Add optional depth parameter to the list_directory tool's Zod schema, inputSchema, and handler to support recursive directory listing.

Problem

Some LLMs (e.g., lmstudio-community/gpt-oss-120b-MLX-8bit) generate a depth argument when calling list_directory, but the schema rejected it as "params is not allowed to have the additional property 'depth'".

Solution

  1. Added depth: z.number().optional() to ListDirectoryArgsSchema
  2. Added depth to the inputSchema in the tool registration
  3. Updated handler to perform recursive listing when depth > 0

Closes #2703

Add optional depth parameter to list_directory tool to support
recursive directory listing. Some LLMs generate a depth argument
for directory listing which was previously rejected as an
"additional property" error.

Fixes: modelcontextprotocol#2703
Copy link
Copy Markdown
Member

@olaservo olaservo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the idea — a depth parameter for list_directory could be useful. However, the current implementation has some issues that need to be addressed:

  1. Runtime crash: listDirRecursive returns an array of formatted strings, but the code after the call still tries to access .isDirectory() and .name on each entry (which are Dirent methods, not string methods). This will throw at runtime.

  2. Infinite recursion when depth is omitted: When depth is undefined (the default), the condition maxDepth === undefined || currentDepth < maxDepth is always true, causing the function to recurse into every subdirectory indefinitely. The tool should behave exactly as before when depth is not provided (list only the immediate directory).

  3. Missing input schema update: The Zod schema is updated but the inline inputSchema in registerTool is not, so the depth parameter won't be advertised to clients.

  4. No tests or CI: No tests are included, and CI doesn't appear to be running on this branch.

  5. No depth cap: Consider adding a maximum depth limit (e.g., 10) to prevent a runaway traversal on large filesystems.


This review was assisted by Claude Code.

@cliffhall cliffhall added bug Something isn't working server-filesystem Reference implementation for the Filesystem MCP server - src/filesystem labels Apr 20, 2026
@cliffhall
Copy link
Copy Markdown
Member

@claude review

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Apr 21, 2026

Claude finished @cliffhall's task in 1m 17s —— View job


PR Review: fix(filesystem): add optional depth parameter to list_directory

  • Gather context and read changed files
  • Analyze the diff and review the implementation
  • Post review feedback

Summary

This PR adds a depth parameter to list_directory to support recursive directory listing. The motivation is valid — some LLMs generate a depth argument and get rejected by the schema. However, the implementation has several correctness bugs that would cause runtime failures.


Issues Found

1. Runtime crash — .isDirectory() called on strings (Critical)

listDirRecursive returns an array of formatted strings (e.g. "[DIR] foo"), but the calling code immediately tries to treat them as Dirent objects:

// listDirRecursive already returns pre-formatted strings like "[DIR] foo"
const entries = await listDirRecursive(validPath, 0, args.depth);

// BUG: entry is a string, not a Dirent. .isDirectory() and .name don't exist on strings.
const formatted = entries
  .map((entry) => `${entry.isDirectory() ? "[DIR]" : "[FILE]"} ${entry.name}`)
  .join("\n");

This will throw TypeError: entry.isDirectory is not a function at runtime on every call. The fix is to remove the .map() entirely and just .join("\n") on the already-formatted strings returned by listDirRecursive. Fix this →


2. Infinite recursion when depth is omitted (Critical)

The recursion guard condition is inverted:

// BUG: when maxDepth is undefined (depth not provided), this is ALWAYS true
if (entry.isDirectory() && (maxDepth === undefined || currentDepth < maxDepth)) {

When a caller omits depth, maxDepth is undefined, so maxDepth === undefined is always true, meaning the function recurses into every subdirectory indefinitely. The condition should be:

if (entry.isDirectory() && maxDepth !== undefined && currentDepth < maxDepth) {

This makes depth actually optional — when omitted, the tool lists only the immediate directory (preserving the original behavior). Fix this →


3. depth not advertised in inputSchema (Minor)

The ListDirectoryArgsSchema Zod object has depth added, but the inputSchema in registerTool does not:

// inputSchema only has path — depth is invisible to clients
inputSchema: {
  path: z.string()   // ← depth missing here
},

The handler uses ListDirectoryArgsSchema for its type but the schema advertised to MCP clients doesn't include depth. Fix this →


4. Missing TypeScript type annotations (Minor)

listDirRecursive parameters have no type annotations, which violates the repo's strict TypeScript requirement:

// TS strict mode will flag these implicit any types
async function listDirRecursive(dirPath, currentDepth, maxDepth) {

Should be:

async function listDirRecursive(dirPath: string, currentDepth: number, maxDepth: number | undefined): Promise<string[]>

5. No depth cap (Minor)

Without an upper bound, a caller can pass depth: 999 and traverse a deeply nested filesystem. Consider capping at a reasonable limit (e.g. 10) and throwing or clamping if exceeded.


6. Functional overlap with directory_tree

The existing directory_tree tool already does unbounded recursive traversal in a structured JSON format. It's worth noting this in the PR — list_directory with depth covers the "flat text" use case, so the overlap is intentional and acceptable, but reviewers should be aware.


Alternative Approach Worth Considering

The original problem is that some LLMs pass depth and get rejected. A minimal fix would be to accept-and-ignore the parameter using Zod's .passthrough(), without implementing the recursive behavior. That avoids the implementation complexity entirely if recursive listing isn't a core feature goal.


Verdict

The idea is sound, but bugs 1 and 2 are both critical and would cause this to crash or hang on every invocation. The PR needs fixes before merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working server-filesystem Reference implementation for the Filesystem MCP server - src/filesystem

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] list_directory params is not allowed to have the additional property depth

3 participants